#!/usr/bin/perl -w

use strict;

## Copyright (C) 2015  Intel Corporation                         ##
#                                                                ##
## This software falls under the GNU General Public License.     ##
## Please read the COPYING file for more information             ##
#
#
# This software reads a XML file and a list of valid interal
# references to replace Docbook tags with links.
#
# The list of "valid internal references" must be one-per-line in the following format:
#      API-struct-foo
#      API-enum-bar
#      API-my-function
#
# The software walks over the XML file looking for xml tags representing possible references
# to the Document. Each reference will be cross checked against the "Valid Internal Reference" list. If
# the referece is found it replaces its content by a <link> tag.
#
# usage:
# kernel-doc-xml-ref -db filename
#		     xml filename > outputfile

# read arguments
if ($#ARGV != 2) {
	usage();
}

#Holds the database filename
my $databasefile;
my @database;

#holds the inputfile
my $inputfile;
my $errors = 0;

my %highlights = (
	"<function>(.*?)</function>",
	    "\"<function>\" . convert_function(\$1, \$line) . \"</function>\"",
	"<structname>(.*?)</structname>",
	    "\"<structname>\" . convert_struct(\$1) . \"</structname>\"",
	"<funcdef>(.*?)<function>(.*?)</function></funcdef>",
	    "\"<funcdef>\" . convert_param(\$1) . \"<function>\$2</function></funcdef>\"",
	"<paramdef>(.*?)<parameter>(.*?)</parameter></paramdef>",
	    "\"<paramdef>\" . convert_param(\$1) . \"<parameter>\$2</parameter></paramdef>\"");

while($ARGV[0] =~ m/^-(.*)/) {
	my $cmd = shift @ARGV;
	if ($cmd eq "-db") {
		$databasefile = shift @ARGV
	} else {
		usage();
	}
}
$inputfile = shift @ARGV;

sub open_database {
	open (my $handle, '<', $databasefile) or die "Cannot open $databasefile";
	chomp(my @lines = <$handle>);
	close $handle;

	@database = @lines;
}

sub process_file {
	open_database();

	my $dohighlight;
	foreach my $pattern (keys %highlights) {
		$dohighlight .=  "\$line =~ s:$pattern:$highlights{$pattern}:eg;\n";
	}

	open(FILE, $inputfile) or die("Could not open $inputfile") or die ("Cannot open $inputfile");
	foreach my $line (<FILE>)  {
		eval $dohighlight;
		print $line;
	}
}

sub trim($_)
{
	my $str = $_[0];
	$str =~ s/^\s+|\s+$//g;
	return $str
}

sub has_key_defined($_)
{
	if ( grep( /^$_[0]$/, @database)) {
		return 1;
	}
	return 0;
}

# Gets a <function> content and add it a hyperlink if possible.
sub convert_function($_)
{
	my $arg = $_[0];
	my $key = $_[0];

	my $line = $_[1];

	$key = trim($key);

	$key =~ s/[^A-Za-z0-9]/-/g;
	$key = "API-" . $key;

	# We shouldn't add links to <funcdef> prototype
	if (!has_key_defined($key) || $line =~ m/\s+<funcdef/i) {
		return $arg;
	}

	my $head = $arg;
	my $tail = "";
	if ($arg =~ /(.*?)( ?)$/) {
		$head = $1;
		$tail = $2;
	}
	return "<link linkend=\"$key\">$head</link>$tail";
}

# Converting a struct text to link
sub convert_struct($_)
{
	my $arg = $_[0];
	my $key = $_[0];
	$key =~ s/(struct )?(\w)/$2/g;
	$key =~ s/[^A-Za-z0-9]/-/g;
	$key = "API-struct-" . $key;

	if (!has_key_defined($key)) {
		return $arg;
	}

	my ($head, $tail) = split_pointer($arg);
	return "<link linkend=\"$key\">$head</link>$tail";
}

# Identify "object *" elements
sub split_pointer($_)
{
	my $arg = $_[0];
	if ($arg =~ /(.*?)( ?\* ?)/) {
		return ($1, $2);
	}
	return ($arg, "");
}

sub convert_param($_)
{
	my $type = $_[0];
	my $keyname = convert_key_name($type);

	if (!has_key_defined($keyname)) {
		return $type;
	}

	my ($head, $tail) = split_pointer($type);
	return "<link linkend=\"$keyname\">$head</link>$tail";

}

# DocBook links are in the API-<TYPE>-<STRUCT-NAME> format
# This method gets an element and returns a valid DocBook reference for it.
sub convert_key_name($_)
{
	#Pattern $2 is optional and might be uninitialized
	no warnings 'uninitialized';

	my $str = $_[0];
	$str =~ s/(const|static)? ?(struct)? ?([a-zA-Z0-9_]+) ?(\*|&)?/$2 $3/g ;

	# trim
	$str =~ s/^\s+|\s+$//g;

	# spaces and _ to -
	$str =~ s/[^A-Za-z0-9]/-/g;

	return "API-" . $str;
}

sub usage {
	print "Usage: $0 -db database filename\n";
	print "         xml source file(s) > outputfile\n";
	exit 1;
}

# starting point
process_file();

if ($errors) {
	print STDERR "$errors errors\n";
}

exit($errors);
